﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Threading;

namespace TXLooker.WorkItemExtention
{
    class BackgroundMultiWorker
    {
        public delegate bool StopWorkChecker(object argument);

        public event DoWorkEventHandler DoIteration;
        public event RunWorkerCompletedEventHandler WorkCompleted;
        private StopWorkChecker stopWorkCheckHandler;

        private AutoResetEvent allWorkCompleted = new AutoResetEvent(false);

        private List<BackgroundWorker> bgWorkers;

        private object iterationArgument;
        private int freeWorkersCount;
        private object syncLock = new object();

        public BackgroundMultiWorker(int workersCount, StopWorkChecker stopWorkChecker)
        {
            if (stopWorkChecker == null)
                throw new ArgumentException("Argument cannot be null.", "stopWorkChecker");

            stopWorkCheckHandler = stopWorkChecker;
            bgWorkers = new List<BackgroundWorker>(workersCount);
            for (int i=0; i<workersCount; i++)
            {
                BackgroundWorker bgWorker = new BackgroundWorker();
                bgWorker.DoWork += workerThread_DoWork;
                bgWorker.RunWorkerCompleted += workerThread_RunWorkerCompleted;
                bgWorkers.Add(bgWorker);
            }
        }

        public WaitHandle RunWorker(object argument)
        {
            iterationArgument = argument;

            allWorkCompleted.Reset();

            freeWorkersCount = bgWorkers.Count;

            foreach (var worker in bgWorkers)
            {
                worker.RunWorkerAsync();
            }

            return allWorkCompleted;
        }

        void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool continueWork = true;
            lock (syncLock)
            {
                ++freeWorkersCount;
                if (stopWorkCheckHandler(iterationArgument))
                {
                    continueWork = false;
                    if (freeWorkersCount == bgWorkers.Count)
                    {
                        allWorkCompleted.Set();
                        if (WorkCompleted != null)
                            WorkCompleted(this, new RunWorkerCompletedEventArgs(true, null, false));
                    }
                }
            }
            if (continueWork)
                ((BackgroundWorker)sender).RunWorkerAsync();
        }

        void workerThread_DoWork(object sender, DoWorkEventArgs e)
        {
            lock (syncLock)
            {
                --freeWorkersCount;
            }
            if (DoIteration != null)
                DoIteration(this, new DoWorkEventArgs(iterationArgument));
        }
    }
}
